package com.sp.scmdb.webservice.suport;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Map.Entry;

import org.exolab.castor.mapping.Mapping;
import org.exolab.castor.xml.Marshaller;
import org.exolab.castor.xml.Unmarshaller;
import org.xml.sax.InputSource;

public class CastorSerializer {

	protected List<String> mappingLocations;
	protected Mapping mapping;
	protected Map<String, String> namespaces;

	public void initUnmarshaller(Unmarshaller unmarshaller) {
		unmarshaller.setIgnoreExtraAttributes(true);
		unmarshaller.setIgnoreExtraElements(true);
	}

	public void initMarshaller(Marshaller marshaller) {
		marshaller.setUseXSITypeAtRoot(false);
		marshaller.setSuppressXSIType(true);
		marshaller.setSuppressNamespaces(true);
	}

	protected Mapping createMapping() {
		try {
			Mapping mapping = new Mapping();
			if (mappingLocations != null) {
				for (String eachMapping : mappingLocations) {
					mapping.loadMapping(Thread.currentThread()
							.getContextClassLoader().getResource(eachMapping));
				}
			}
			return mapping;
		} catch (Exception e) {
			throw new RuntimeException();
		}
	}

	private synchronized void initMappingIfNecessary() {
		if (mapping == null) {
			mapping = createMapping();
		}
	}

	public void marshal(Object object, OutputStream outputStream,
			String encoding) {
		try {
			OutputStreamWriter writer;
			if (encoding == null) {
				encoding = "UTF-8";
			}
			writer = new OutputStreamWriter(outputStream);
			initMappingIfNecessary();
			Marshaller marshaller = new Marshaller();
			synchronized (mapping) {
				marshaller.setMapping(mapping);
			}
			marshaller.setWriter(writer);
			if (namespaces != null) {
				Set<Entry<String, String>> entrySet = namespaces.entrySet();
				for (Entry<String, String> each : entrySet) {
					marshaller.setNamespaceMapping(each.getKey(), each
							.getValue());
				}
			}
			initMarshaller(marshaller);
			marshaller.marshal(object);
		} catch (Exception e) {
			throw new RuntimeException(e);
		}
	}

	public Object unmarshal(InputStream inputStream, String encoding) {
		InputSource inputSource = new InputSource(inputStream);
		if (encoding != null) {
			inputSource.setEncoding(encoding);
		}
		return unmarshal(inputSource);
	}

	public Object unmarshal(InputSource inputSource) {
		initMappingIfNecessary();
		try {
			Unmarshaller unmarshaller = new Unmarshaller();
			synchronized (mapping) {
				unmarshaller.setMapping(mapping);
			}
			initUnmarshaller(unmarshaller);
			return unmarshaller.unmarshal(inputSource);
		} catch (Exception e) {
			throw new RuntimeException(e);
		}
	}

	public synchronized void addMappingLocation(String mappingLocation) {
		if (mappingLocations == null) {
			mappingLocations = new ArrayList<String>();
		}
		mappingLocations.add(mappingLocation);
	}

	public synchronized void addNamespace(String key, String value) {
		if (namespaces == null) {
			namespaces = new HashMap<String, String>();
		}
		namespaces.put(key, value);
	}

}
